package com.test.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledDirectByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;

import java.util.Date;

/**
 * describe: 处理websocket请求的核心类
 *
 * @author lianying
 * @date 2020/06/14
 */
public class MyWebSocketHandler extends SimpleChannelInboundHandler<Object> {
    private WebSocketServerHandshaker webSocketServerHandshaker;
    private static final String WEB_SOCKET_URL="ws://localhost:8888/websocket";

    @Override
    protected void channelRead0(ChannelHandlerContext context, Object msg) throws Exception {
        //处理客户端向服务端发起http握手请求的业务
        if(msg instanceof FullHttpRequest)
        {
            handHttpRequest(context, (FullHttpRequest) msg);
            //处理WebSocket业务
        }else if(msg instanceof WebSocketFrame)
        {
            handWebSocket(context, (WebSocketFrame) msg);
        }
    }

    /**
     * 处理websocket业务
     *
     * @param context
     * @param frame
     */
    private void handWebSocket(ChannelHandlerContext context, WebSocketFrame frame) {
        if(frame instanceof CloseWebSocketFrame)
        {
            webSocketServerHandshaker.close(context.channel(),((CloseWebSocketFrame) frame).retain());
            return;
        }else if(frame instanceof PingWebSocketFrame)
        {
            context.channel().writeAndFlush(new PingWebSocketFrame(frame.content().retain()));
            return;
        }else if(!(frame instanceof TextWebSocketFrame))
        {
            throw new RuntimeException("不支持二进制消息");
        }
        String text = ((TextWebSocketFrame) frame).text();
        System.out.println(text);
        TextWebSocketFrame textWebSocketFrame=new TextWebSocketFrame(new Date().toString()+"--->"+text);
        NettyConfig.group.writeAndFlush(textWebSocketFrame);
    }

    /**
     * 客户端与服务端创建连接时调用
     *
     * @param context
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext context) throws Exception {
        NettyConfig.group.add(context.channel());
        System.out.println("客户端建立连接："+context.channel().id());
    }

    /**
     * 客户端与服务端断开连接时调用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.group.remove(ctx.channel());
        System.out.println("客户端断开连接："+ctx.channel().id());
    }

    /**
     * 服务端接收客户端发送数据时调用
     *
     * @param context
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext context) throws Exception {
        context.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.writeAndFlush("连接异常");
        ctx.close();
    }

    /**
     * 处理握手请求的业务
     *
     * @param context
     * @param request
     */
    public void handHttpRequest(ChannelHandlerContext context,FullHttpRequest request){
        if(!request.getDecoderResult().isSuccess()||!("websocket".equals(request.headers().get("Upgrade"))))
        {
            sendResponse(context,request,new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        WebSocketServerHandshakerFactory factory=new WebSocketServerHandshakerFactory(WEB_SOCKET_URL,null,false);
        webSocketServerHandshaker=factory.newHandshaker(request);
        if(webSocketServerHandshaker==null)
        {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(context.channel());
        }else {
            webSocketServerHandshaker.handshake(context.channel(),request);
        }
    }

    public void sendResponse(ChannelHandlerContext context, FullHttpRequest request, DefaultFullHttpResponse response){
        if(response.getStatus().code()!=200)
        {
            ByteBuf byteBuf= Unpooled.copiedBuffer(response.getStatus().toString(), CharsetUtil.UTF_8);
            response.content().writeBytes(byteBuf);
            byteBuf.release();
        }
        ChannelFuture channelFuture=context.channel().writeAndFlush(response);
    }
}
